/*
  Program: Mine Mayhem
  File:    minefld.h
  Date:    July, 1996 to February,   1997,    version 1.00
	   26 October to 3 December,	      version 2.00
	   8 December to 4 January,  2001,    version 2.10
	   15 January,			      version 2.11
	   18 September to 16 December, 2006, version 2.90

  Definitions for the minefield.

  Freeware, Copyright 2006 Jason Hood.
*/

#ifndef _minefield_h
#define _minefield_h

#include "mine.h"
#include "layout.h"
#include "custom.h"
#include "tiles.h"
#include "inifile.h"
#include "times.h"
#include "tree234.h"


// A tile on the minefield
struct Tile
{
  int	 mine;				// 0 for no mine, 1 for mine
  int	 mines; 			// Number of mines on/around the tile
  int	 marks; 			// Number of flags on/around the tile
  int	 status;
  int	 hint;

  Coord  grid;				// Grid location for the solver
  Coord  loc;				// Displayed location

  Tile** surround;			// The tile and its neighbours
  int	 mask;				// Bitmask of the n'bours for the solver
  Tile*  next;				// Next tile on the solver to-do
};

enum					// Values for status, hint
{					//  and DisplayTile
  OPENED,				// Or zero
  /* 1 to 9 */				// Opened tile of equivalent value
  MINE = 10,
  WRONGMINE,
  DEADMINE,
  UNOPENED,
  HINT,
  HINTMARK,
  HINTOPEN,
  HINTMARKX,				// Wrong mark
  HINTMARKU,				// Unknown mark
  MARKED,
  /* 1 to 9 */				// Marked tile of equivalent value
};


#define WON  1				// Values for isFinished
#define LOST 2


// Structures used by the solver.
struct squaretodo
{
  Tile *head, *tail;
  squaretodo() : head(0), tail(0) {}
  void add(Tile* t);
  Tile* extract();
};

struct setstore
{
  tree234 *sets;
  struct set *todo_head, *todo_tail;
  struct set **list;
  int listsize;
  setstore();
  ~setstore();
  void clear();
  void add(int x, int y, int mask, int mines);
  void add_todo(struct set *s);
  void remove(struct set *s);
  struct set **overlap(int x, int y, int mask);
  struct set *todo();
};

extern const int bit[3][3];


class Minefield
{
public:
	   Minefield();
  virtual ~Minefield();

  virtual int	area(cCoord& dim) const = 0;

  virtual bool	canWrap() const
	  {
	    return (isWrap && Game == CLEAR && Start != PERIMETER);
	  }

	  void	SetLevel(int Level, int from_ini = true);
	  void	NewGame(int Level = 0);
  virtual void	AdjustWrap() {};	// Changing the wrap-around option

	  // Co-ordinate movement and comparison needed by the solver with wrap.
  virtual int	addx(int x, int dx)  const { return x + dx; }
  virtual int	addy(int y, int dy)  const { return y + dy; }
  virtual int	cmpx(int x1, int x2) const { return x1 - x2; }
  virtual int	cmpy(int y1, int y2) const { return y1 - y2; }

	  int	Progress()     const;
	  int	GameProgress() const;
	  bool	GameWon()      const { return isFinished == WON; }
	  int	FlagsLeft()    const { return Game == CROSS ? marked : mines;  }
	  int	SpacesLeft()   const { return Game == CROSS ? opened : spaces; }
	  void	Solve();
	  void	ShowHints();

	  bool	Convert(int x, int y, Tile*& t);	// Mouse co-ord to tile
	  int	LeftButton(Tile* t);
	  int	RightButton(Tile* t);
	  void	UnHighlight();

  virtual Coord GetBoardDimensions(cCoord& dim) = 0;
  virtual void	Outline(ViewBuffer* buf, int x1, int y1, int x2, int y2) = 0;
	  void	Pause();

	  int	Find3BV(bool opened = false);
	  int	Opened() const { return opened; }
	  int	Marked() const { return marked; }

  // By rights these should be protected, with public functions to access them,
  // but I couldn't be bothered.

  Layout Beginner,			// These three should be obvious
	 Intermediate,
	 Expert,
	 Custom,			// Initially from the initialisation
	 Random,			//  file (hmmm... :) )
	 layout;			// The layout being played
  int	 cols, rows;			// Current grid dimensions

  // The minimum and maximum dimensions the minefield can have.
  int MinWidth, MaxWidth, MinHeight, MaxHeight;

  CustomPopupRes CustomPopup;		// The stored custom games
  Times times;				// All the times for all the levels

  int	level;				// Current level
  bool	isRestarted;			// The game is being played again
  int	score;				// Time (in milliseconds)

protected:
	  void	ReadIni( IniFile& ini, const char* section, const char* file);
	  void	WriteIni(IniFile& ini, const char* section, const char* file);
  virtual void	Validate(Layout& layout);

	  void	CreateMinefield();

  virtual void	EveryTile() = 0;
  virtual void	Surround(Tile* t) = 0;
	  void	Surround(Tile* t, int num, cCoord ofs[]);

	  void	RandomLayout(int diff);
  virtual void	RandomDim() = 0;

  virtual void	Convert(int& x, int& y) = 0;	// Mouse co-ord to grid co-ord

	  bool	PlaceMine();
	  bool	MoveMine(Tile* t);
	  bool	isUnknown(Tile* t);
	  bool	AllMarked(Tile* t);

	  void	Open(Tile* t);
	  bool	Clear(Tile* t);
	  bool	ClearAround(Tile* t, bool doHilite = false);
	  bool	MarkAround(Tile* t, bool doHilite = false);
	  void	Highlight(Tile* t, int what);
	  void	Mark(Tile* t);
	  bool	GameOver(bool dead = false);

	  void	MineCount(Tile* t, int delta);
	  void	MarkCount(Tile* t, int delta);
	  int	ShowCount(Tile* t);

  virtual void	DisplayTile(Tile* t, int which) = 0;
	  void	DisplayTile(Tile* t, int which,
			    int w, int h, ViewBuffer* buf[]);

	  int	PanLevel(cCoord& c) const;	// Panning for sound

	  // Solver functions.
	  void	Known(bool hint, int x, int y, int mask, int mines);
	  void	CreateSet(int hint, int x, int y, int mask, int mines);
	  int	Solve(int* mineset, int minecnt);
	  struct perturbations* Perturb(int x, int y, int mask,
				       int* mineset, int minecnt);
	  bool	Generate(int* mineset, int minecnt);

  Tile*   field;			// The tiles on the minefield
  int	  tile_count;			// Number of above
  int*	  perimeter;			// List of tiles on the perimeter
  Tile*** grid; 			// Grid of pointers for each tile
  int	  max_cols, max_rows;		// Available grid dimensions

  bool	 isStarted;			// The game has begun
  int	 isFinished;			// The game is over
  Tile*  starttile;			// The initial opened tile for restart

  int	 spaces,			// Spaces cleared
	 mines, 			// Mines flagged
	 mynes, 			// Mines found
	 opened,			// Tiles opened (and cleared) by user
	 marked;			// Tiles marked (and unmarked) by user

  Tile*  ht;				// The highlight centre position
  int	 distance;			// Minimum distance to finish cross

  int	 pan_wid;			// Value to determine panning level

  squaretodo std;			// Tiles to process for the solver
  setstore ss;				// Sets to process for the solver
  int tileclues,			// Tiles determined by the solver
      mineclues,			// Mines determined by the solver
      minestogo;			// Mines yet to be determined
};

// These are meant for private use.
#define on_around(t, n) \
for (Tile** n = (t)->surround; *n; ++n)

#define around(t, n) \
for (Tile** n = (t)->surround + 1; *n; ++n)

#define foralltiles \
for (int tile = 0; tile < tile_count; ++tile)


class SquareField : public Minefield
{
public:
  SquareField(bool create = true);
  void	ReadIni( IniFile& ini);
  void	WriteIni(IniFile& ini);

  int	area(cCoord& dim) const;

  void	AdjustWrap();

  int	addx(int x, int dx)  const;
  int	addy(int y, int dy)  const;
  int	cmpx(int x1, int x2) const;
  int	cmpy(int y1, int y2) const;

  Coord GetBoardDimensions(cCoord& field);
  void	Outline(ViewBuffer* buf, int x1, int y1, int x2, int y2);

protected:
  void	EveryTile();
  void	Surround(Tile* t);

  void	RandomDim();

  void	Convert(int& x, int& y);

  void	DisplayTile(Tile* t, int which);

  int	PanLevel(cCoord& c);

  void	Validate(Layout& layout);
};


class RectHexField : public SquareField
{
public:
  RectHexField();
  void	ReadIni( IniFile& ini);
  void	WriteIni(IniFile& ini);

  bool	canWrap() const
  {
    // Only fields with an even height are capable of wrapping.
    return (!(layout.dim.y & 1) && Minefield::canWrap());
  }

  Coord GetBoardDimensions(cCoord& field);
  void	Outline(ViewBuffer* buf, int x1, int y1, int x2, int y2);

protected:
  void	EveryTile();
  void	Surround(Tile* t);

  void	Convert(int& x, int& y);

  void	DisplayTile(Tile* t, int which);

  int	PanLevel(cCoord& c);
};


class HexagonField : public Minefield
{
public:
  HexagonField();
  void	ReadIni( IniFile& ini);
  void	WriteIni(IniFile& ini);

  int	area(cCoord& dim) const;

  bool	canWrap() const { return false; }

  Coord GetBoardDimensions(cCoord& field);
  void	Outline(ViewBuffer* buf, int x1, int y1, int x2, int y2);

  int	GetMaxWidth(int height);

protected:
  void	EveryTile();
  void	Surround(Tile* t);

  void	RandomDim();

  void	Convert(int& x, int& y);

  void	DisplayTile(Tile* t, int which);

  int	PanLevel(cCoord& c);

  void	Validate(Layout& layout);
};


extern Minefield*    field;
extern SquareField*  squarefield;
extern RectHexField* recthexfield;
extern HexagonField* hexagonfield;

#endif
